#include"Midi.h"

//将32位小端模式读取的数据转换成大端模式
unsigned int little2big32(unsigned int le) {
	return (le & 0xff) << 24
		| (le & 0xff00) << 8
		| (le & 0xff0000) >> 8
		| (le >> 24) & 0xff;
}

//将16位小端模式读取的数据转换成大端模式
unsigned short little2big16(unsigned short num)
{
	unsigned short swapped = (num >> 8) | (num << 8);
	return swapped;
}

Midi::Midi()
{
	midiOutOpen(&handle, 0, 0, 0, 0);
	for (int i = 0; i < 16; i++) {
		tracks[i].track_id = i;
	}
}
Midi::~Midi() {
	midiOutClose(handle);
}
void Midi::program_change(int program,int channel)
{
	int msg = (program << 8) + (PROGRAM_CHANGE << 4) + channel;
	midiOutShortMsg(handle, msg);
}
void Midi::note_on(int note, int volume, int channel, int time) {
	Sleep(time);
	int msg = (volume << 16) + (note << 8) + (MIDICMD::NOTE_ON<< 4) + channel;
	midiOutShortMsg(handle, msg);
	
}
void Midi::note_off(int note, int volume, int channel, int time) {
	Sleep(time);
	int msg = (volume << 16) + (note << 8) + (MIDICMD::NOTE_OFF << 4) + channel;
	midiOutShortMsg(handle, msg);
}

void Midi::play_track(Track track) {
	for (auto msg : track.messages) {
		switch (msg.cmd)
		{
		case MIDICMD::NOTE_OFF:
			this->note_off(msg.flip, msg.velocity, msg.channel, msg.time);
			break;
		case MIDICMD::NOTE_ON:
			this->note_on(msg.flip, msg.velocity, msg.channel, msg.time);
			break;
		case MIDICMD::PROGRAM_CHANGE:
			this->program_change(msg.program, msg.channel);
		default:
			break;
		}
	}
}
void Midi::play() {
	for (int i = 0; i < 16;i++) {
		if (tracks[i].message_num <= 0)continue;
		std::thread t(&Midi::play_track, this, tracks[i]);
		t.detach();
	}
	Sleep(this->midi_time()+3000);
}

int Midi::midi_time() {
	int max_time = 0;
	for (auto track : this->tracks) {
		max_time = track.track_time > max_time ? track.track_time : max_time;
	}
	return max_time;
}
int Midi::tracks_num() {
	int num = 0;
	for (int i = 0; i < 16; i++) {
		if (tracks[i].message_num)num++;
	}
	return num;
}
void Midi::write_file(const char* file_path) {
	FILE*f = fopen(file_path, "wb");
	fwrite("MThd", 4, 1, f);
	int _infoLenth = little2big32(0x00000006);
	short _type = little2big16(0x0001);
	short _division = little2big16(480);
	short _tracks = little2big16(tracks_num());
	fwrite(&_infoLenth, 4, 1, f);
	fwrite(&_type, 2, 1, f);
	fwrite(&_tracks, 2, 1, f);
	fwrite(&_division, 2, 1, f);

	for (auto t : tracks) {
		if (t.message_num == 0)continue;
		fwrite("MTrk", 4, 1, f);
		int _size = little2big32(t.size);
		fwrite(&_size, 4, 1, f);
		for (auto m : t.messages) {
			//时间写入处理
			if (m.time > 0x80) {
				unsigned char _times = m.time / 128;
				unsigned char code1 = 0x80 + _times;
				unsigned char code2 = m.time % 128;
				fwrite(&code1, 1, 1, f);
				fwrite(&code2, 1, 1, f);
			}
			else {
				fwrite(&m.time, 1, 1, f);
			}
			//meta消息写入处理
			unsigned char code = (m.cmd << 4) + m.channel;
			fwrite(&code, 1, 1, f);
			switch (m.cmd)
			{
			case MIDICMD::NOTE_OFF:
				fwrite(&m.flip, 1, 1, f);
				fwrite(&m.velocity, 1, 1, f);
				break;
			case MIDICMD::NOTE_ON:
				fwrite(&m.flip, 1, 1, f);
				fwrite(&m.velocity, 1, 1, f);
				break;
			case MIDICMD::PROGRAM_CHANGE:
				fwrite(&m.program, 1, 1, f);
			default:
				break;
			}

		}
		int end = little2big32(0x00ff2f00);
		fwrite(&end, 4, 1, f);

	}
}
